#include <assert.h>
#include <ctype.h>
#include <locale.h>
#include <errno.h>
#include <float.h>
#include <inttypes.h>
#include <limits.h>
#include <math.h>
#include <pthread.h>
#include <setjmp.h>
#include <signal.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdnoreturn.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include<windows.h>

/*
A C macro is just a preprocessor command
that is defined using the #define preprocessor directive. 

During the preprocessing stage, the C preprocessor 
(a part of the C compiler) 
simply substitutes the body of the macro wherever its name appears.
*/

/*
The important thing is to ensure that the convention you follow makes 
it so that each file in your project has a unique header guard.

Header include guard

#ifndef HEADER_1_H
#define HEADER_1_H
    typedef struct MyStruct MyStruct;
    int myFunction(MyStruct *value);
#endif
*/

/*
#if 0 to block out code sections

*/
#if 0
/* #if 0 evaluates to false, so everything between here and the #endif are
* removed by the preprocessor. */
int myUnusedFunction(void)
{
    int i = 5;
    return i;
}
#endif

// Function-like macros
// Function-like macros are similar to inline functions, 
// these are useful in some cases, such as temporary debug log:
#define DEBUG true;
#ifdef DEBUG
    # define LOGFILENAME "logfile.log"
    # define LOG(str) do { \
        FILE *fp = fopen(LOGFILENAME, "a"); \
        if (fp) { \
            printf("write content to file: %s\n", LOGFILENAME);\
            fprintf(fp, "%s:%d %s\n", __FILE__, __LINE__, \
            /* don't print null pointer */ \
            str ?str :"<null>"); \
            fclose(fp); \
        } \
        else { \
            perror("Opening '" LOGFILENAME "' failed"); \
        } \
    } while (0)
#else
    /* Make it a NOOP if DEBUG is not defined. */
    # define LOG(LINE) (void)0
#endif

// If you use GCC, you can also implement a function-like macro 
// that returns result using a non-standard GNU extension
#define POW(X, Y) \
({ \
    int i, r = 1; \
    for (i = 0; i < Y; ++i) \
    r *= X; \
    r; \
}) // returned value is result of last operation

/*
Source file inclusion
    #include <stdio.h>
    #include "myheader.h"
Angle brackets (<>) refer to header files installed on the system, 
while quotation marks ("") are for user-supplied files.

*/
#if VERSION == 1
    #define INCFILE "vers1.h"
#elif VERSION == 2
    #define INCFILE "vers2.h"
    /* and so on */
#else
    #define INCFILE "versN.h"
#endif
/* ... */
// #include INCFILE

void token_pasting(void)
{
    printf("***** token_pasting *****\n");
    #define UNICODE
    #ifdef UNICODE
        #define TEXT(x) printf("%d\n", 123##x)
        // TEXT(4) --> 1234
    #endif
    TEXT(4);
}

void variadic_arguments_macro(void)
{
    printf("***** variadic_arguments_macro *****\n");
    #define debug_printf(msg) printf("%s:%d %s\n", __FILE__, __LINE__, msg)
    
    debug_printf("somefunc() has failed");
    /*
    What happens if the implementation of somefunc() changes, 
    and it now returns different values matching different possible error types? 
    You still want use the debug macro and print the error value.
    */
    // debug_printf(-1); /* type error: this would obviously fail */
    // debug_printf("%d",-1); /* argument error: this would also fail */

    // To solve this problem the __VA_ARGS__ macro was introduced. 
    // This macro allows multiple parameters X-macro's:
    #define debug_print(msg, ...) printf(msg, ##__VA_ARGS__); \
                                  printf("\nError occurred in file:line (%s:%d)\n", __FILE__, __LINE__)

    debug_print("Ret val of somefunc()?");
    debug_print("%d",-1);
}

void macro_replacement(void)
{
    printf("***** macro_replacement *****\n");
    #define ARRSIZE 100
    int array[ARRSIZE];

    #define TIMES10(A) ((A) *= 10)
    double b = 34;
    int c = 23;
    TIMES10(b); // good: ((b) *= 10);
    printf("A * 10: %f\n", b);
    TIMES10(c); // good: ((c) *= 10);
    printf("A * 10: %d\n", c);
    // TIMES10(5); // bad: ((5) *= 10);

    #define max(a, b) ((a) > (b) ? (a) : (b))
    int maxVal = max(11, 43); /* 43 */
    int maxValExpr = max(11 + 36, 51 - 7); /* 47 */
    /* Should not be done, due to expression being evaluated twice */
    int j = 0, i = 0;
    int sideEffect = max(++i, ++j); /* i == 4 */
    printf("sideEffect: %d\n", sideEffect);
}

/*
If the preprocessor encounters an #error directive, 
compilation is halted and the diagnostic message included is printed
*/
void error_directive(void)
{
    printf("***** error_directive *****\n");
    #define DEBUG
    #ifdef DEBUG
        #error "Debug Builds Not Supported"
    #endif
}

void main(int argc, char* argv[])
{
    if (argc > 1)
        LOG("There are command line arguments");
    else
        LOG("No command line arguments");

    int result;
    result = POW(2,3);
    printf("Result: %d\n", result);

    token_pasting();

    variadic_arguments_macro();

    macro_replacement();

    // error_directive();
    system("pause");
}